package io.task.message.socket;

import io.task.message.socket.ChannelContext.CloseCode;
import io.task.message.socket.WriteCompletionHandler.WriteCompletionHandlerVo;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Setter
@Getter
public class WriteCompletionHandler implements CompletionHandler<Integer, WriteCompletionHandlerVo> {

    private ChannelContext channelContext = null;
    public final ReentrantLock lock = new ReentrantLock();
    public final Condition condition = lock.newCondition();

    public static class WriteCompletionHandlerVo {
        private ByteBuffer byteBuffer = null;

        private Object object = null;


        public WriteCompletionHandlerVo(ByteBuffer byteBuffer, Object object) {
            super();
            this.byteBuffer = byteBuffer; //[pos=0 lim=212 cap=212]
            this.object = object;
        }
    }

    public WriteCompletionHandler(ChannelContext channelContext) {
        this.channelContext = channelContext;
    }

    @Override
    public void completed(Integer bytesWritten, WriteCompletionHandlerVo writeCompletionHandlerVo) {

        if (bytesWritten > 0) {
            channelContext.stat.latestTimeOfSentByte = System.currentTimeMillis();
        }
        if (writeCompletionHandlerVo.byteBuffer.hasRemaining()) {
            if (log.isInfoEnabled()) {
                log.info("{} {}/{} has sent", channelContext, writeCompletionHandlerVo.byteBuffer.position(), writeCompletionHandlerVo.byteBuffer.limit());
            }
            channelContext.asynchronousSocketChannel.write(writeCompletionHandlerVo.byteBuffer, writeCompletionHandlerVo, this);
        } else {
            handle(bytesWritten, null, writeCompletionHandlerVo);
        }
    }

    @Override
    public void failed(Throwable throwable, WriteCompletionHandlerVo writeCompletionHandlerVo) {

        handle(0, throwable, writeCompletionHandlerVo);
    }


    public void handle(Integer bytesWritten, Throwable throwable, WriteCompletionHandlerVo writeCompletionHandlerVo) {
        ReentrantLock lock = channelContext.writeCompletionHandler.lock;
        lock.lock();
        try {
            channelContext.sendRunnable.canSend = true;
            channelContext.writeCompletionHandler.condition.signal();
            channelContext.stat.latestTimeOfSentPacket = System.currentTimeMillis();
            Object attachment = writeCompletionHandlerVo.object;//();
            boolean isSentSuccess = bytesWritten > 0;


            try {
                boolean isPacket = attachment instanceof Packet;
                if (isPacket) {
                    if (isSentSuccess) {

                    }
                    handleOne((Packet) attachment, isSentSuccess);
                } else {
                    List<?> ps = (List<?>) attachment;
                    for (Object obj : ps) {
                        handleOne((Packet) obj, isSentSuccess);
                    }
                }

                if (!isSentSuccess) {
                    Io.close(channelContext, throwable, "写数据返回:" + bytesWritten, CloseCode.WRITE_COUNT_IS_NEGATIVE);
                }
            } catch (Throwable e) {
                log.error(e.toString(), e);
            }

        } finally {
            lock.unlock();
        }

    }


    public void handleOne(Packet packet, Boolean isSentSuccess) {
        PacketMetaData packetMetaData = packet.getPacketMetaData();
        if (packetMetaData != null) {
            packetMetaData.setIsSentSuccess(isSentSuccess);
        }

        try {
            channelContext.processAfterSent(packet, isSentSuccess);
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }

    }

}
